package channel

import (
	"context"

	ds "gitee.com/shipnet/flogo_edge_channel_data_store"
	"github.com/project-flogo/core/data/metadata"
	"github.com/project-flogo/core/support/log"
	"github.com/project-flogo/core/trigger"
)

var triggerMd = trigger.NewMetadata(&Settings{}, &HandlerSettings{}, &Output{})

func init() {
	_ = trigger.Register(&Trigger{}, &Factory{})
}

type Factory struct {
}

// Metadata implements trigger.Factory.Metadata
func (*Factory) Metadata() *trigger.Metadata {
	return triggerMd
}

// New implements trigger.Factory.New
func (*Factory) New(config *trigger.Config) (trigger.Trigger, error) {
	settings := Settings{}
	err := metadata.MapToStruct(config.Settings, &settings, true)
	if err != nil {
		return nil, err
	}
	t := Trigger{
		settings: settings,
	}
	return &t, nil
}

type Trigger struct {
	// store ds.EdgeChannelStore
	settings Settings
	handlers []trigger.Handler
	sub      ds.Subscription
	logger   log.Logger
	stopC    chan int
}

func (t *Trigger) Initialize(ctx trigger.InitContext) error {
	st := ds.StoreSettings{
		Addr:      t.settings.Addr,
		DB:        t.settings.DB,
		Password:  t.settings.Password,
		KeyPrefix: t.settings.KeyPrefix,
	}
	store, err := ds.New(st)
	if err != nil {
		return err
	}
	topic := t.settings.Sub
	if topic == "" {
		topic = "*"
	}
	sub, err := store.Subscribe(topic)
	if err != nil {
		return err
	}
	t.sub = sub
	// validate handlers
	t.handlers = ctx.GetHandlers()
	t.logger = ctx.Logger()
	t.stopC = make(chan int)
	return nil
}

// Stop implements util.Managed.Start
func (t *Trigger) Start() error {
	go func() {
		for {
			select {
			case msg := <-t.sub.Channel():
				{
					data := map[string]interface{}{
						"data": msg.ToMap(),
					}
					for _, h := range t.handlers {
						_, err := h.Handle(context.TODO(), data)
						if err != nil {
							t.logger.Error(err)
						}
					}
				}
			case <-t.stopC:
				return
			}
		}
	}()
	return nil
}

// Stop implements util.Managed.Stop
func (t *Trigger) Stop() error {
	t.sub.Unsubscribe()
	t.stopC <- 1
	return nil
}
